#include <iostream>
#include <fstream>
#include <iomanip>
#include "header.h"

using namespace std;

char objFilePath[100];

int main(int argc, char *argv[])
{
	int ishalt;
	
	strcpy_s (objFilePath,argv[1]);
	
	//strcpy_s (objfilename,"I:\\ECE486\\Project1\\test.obj");

	debug = DEBUG;
	debugparam = DEBUGPARAM;
	debugfile = DEBUGFILE;

	unsigned short inst = 0; //store current instruction

	initialize();
	if(!readobj(objFilePath))
	{
		do
		{
			if(debug)
			{
				if(!debugfile)
				{
					cout << "Hit Enter for next instruction:";
					cin.get();

					cout << endl << "Instruction #" << totalInst;
					
				}
				else
				{
					ofstream fout;

					fout.open("I:\\ECE486\\Project1\\testoutput.txt",fstream::app);	//append testoutput.txt

					fout << endl << "Instruction #" << totalInst;
					
				}
			}

			inst = fetch();
			
			ishalt = decode(inst);

			if(debug)
				displayregs(debugfile);


		} while(!ishalt);

		if(debug)
			displaymemory(2);

		initiatetrace();

		displaystatistics();
	}
	else
	
	return 0;
}


//initialize registers, counters and other variables
int initialize()
{
	int i;

	LB_AC = 0;
	MQ    = 0;
	PC    = 0;
	IR    = 0;
	MB    = 0;
	CPMA  = 0;
	SR    = 0;

	//inst  = 0;


	ANDcount   = 0;
	TADcount   = 0;
	ISZcount   = 0;
	DCAcount   = 0;
	JMScount   = 0;
	JMPcount   = 0;
	IOcount    = 0;
	microcount = 0;
	totalInst  = 0;
	extraMemCycCount = 0;
	totalCycCount    = 0;
	memWriteCount    = 0;
	memReadCount     = 0;

	for (i=0; i<4096; ++i)
		memory[i] = 0;
	

	return 0;
}

int readobj(char* objfilename)
{
	unsigned int upper, lower;
	unsigned short word, memindex;

	upper = 0;
	lower = 0;
	word  = 0;
	memindex = 0;
	int result(0);

	fstream fin;

	fin.open(objfilename);
	
	fin.setf( ios_base::oct, ios_base::basefield );

	fin >> upper;
	fin.get();
	fin >> lower;
	fin.get();

	if(!fin.eof())
	{
		// Load PC with the first instruction
		// assuming that the fist line is starting address
		PC = (upper << 6 | lower) & 0007777;
		
		do
		{
			word = upper << 6 | lower;
			word &= 0007777;

			if(!(upper & 000000100))
			{
				if(debug & !debugfile)
				{
					cout << setfill('0') << setw(6);
					cout << oct << word <<endl;
				}

				memory[memindex++] = word;
			}
			else
				memindex = word;

			fin >> upper;
			fin.get();
			fin >> lower;
			fin.get();
				

		} while(!fin.eof());
	}
	else
	{
		cout << "\n*Object File Empty*\n\n";
		result = 1;
	}

	fin.close();

	if(debug)
		displaymemory(1);
	
	return result;
}

int displayregs(int debugfile)
{
	if(!debugfile)
	{
		cout << "\n======================\n";

		cout << oct;

		cout << setfill('0') << setw(4);

		cout << "PC     : " << PC << endl;

		cout << dec;

		cout << "IR     : " << IR << endl;

		cout << oct;

		cout << "LB_AC  : ";

		cout << setfill('0') << setw(5);

		cout << LB_AC << 'o' << endl;
	}
	else
	{
		ofstream fout;

		fout.open("I:\\ECE486\\Project1\\testoutput.txt",fstream::app);	//append testoutput.txt


		fout << "\n======================\n";

		fout << oct;

		fout << setfill('0') << setw(4);

		fout << "PC     : " << PC << endl;

		fout << dec;

		fout << "IR     : " << IR << endl;

		fout << oct;

		fout << "LB_AC  : ";

		fout << setfill('0') << setw(5);

		fout << LB_AC << 'o' << endl;
	}
	

	return 0;
}

int displaymemory(int debugparam)
{
	ofstream fout;
	
	switch(debugparam)
	{
	case 1:
	fout.open("I:\\ECE486\\Project1\\test_pre.txt"); break;
	default:
	fout.open("I:\\ECE486\\Project1\\test_post.txt"); break;
	}

	int i;

	for (i = 0; i < 4096; ++i)
	{
		fout << setfill('0') << setw(4);

		fout << oct << i << ' ';

		fout << setfill('0') << setw(6);

		fout << oct << memory[i] << endl;
	}

	fout.close();

	return 0;
}


//fetch instructino from memory
unsigned short fetch()
{
	unsigned short inst;
	
	inst = memory[PC];
	createTrace(FETCH,PC++);

	return (inst & 007777);
}

// Decode and Execute the instruction
int decode(unsigned short inst)
{
	int ishalt = 0;

	// Decoding instruction
	IR = (inst & 007000) >> 9;

	// Execute instruction
	switch (IR)
	{
		case 0: AND(inst); ++ANDcount; break;
		case 1: TAD(inst); ++TADcount; break;
		case 2: ISZ(inst); ++ISZcount; break;
		case 3: DCA(inst); ++DCAcount; break;
		case 4: JMS(inst); ++JMScount; break;
		case 5: JMP(inst); ++JMPcount; break;
		case 6:  IO(inst); ++IOcount;  break;
		case 7: 
				if(inst == 07402)
					ishalt = 1;
				else
					opcode7(inst);

				++microcount; break;
		default:;
	}
	
	if (IR <= 6)
		updateExtraMemCyc(inst);

	++totalInst;	//increment total instruction count

	return ishalt;
}

// Display simulation statistics
int displaystatistics()
{
 //The total number of instructions executed
 //The total number of clock cycles consumed
 //The number of times each instruction type was executed

	totalCycCount = (ANDcount + TADcount + ISZcount + DCAcount + JMScount) * 2 + JMPcount + microcount + extraMemCycCount;

	cout << endl;
	cout << "**************STATISTICS**************" << endl;
	cout << "Total Instructions: " << totalInst << endl;
	cout << "  AND  : " << ANDcount << endl;
	cout << "  TAD  : " << TADcount << endl;
	cout << "  ISZ  : " << ISZcount << endl;
	cout << "  DCA  : " << DCAcount << endl;
	cout << "  JMS  : " << JMScount << endl;
	cout << "  JMP  : " << JMPcount << endl;
	cout << "  IO   : " << IOcount << endl;
	cout << "  Micro: " << microcount << endl;
	cout << "Total Cycles :" << totalCycCount << endl;
	cout << "***************************************" << endl;

	return 0;
}

int updateExtraMemCyc( unsigned short inst ) 
 { 
   unsigned short mask = 0777; 
   
   inst = inst & mask; 
  
   if( inst & 0400 ) 
     switch( inst ) 
     { 
       case 0410 : extraMemCycCount += 2; 
                   break; 
       case 0411 : extraMemCycCount += 2; 
                   break; 
       case 0412 : extraMemCycCount += 2; 
                   break; 
       case 0413 : extraMemCycCount += 2; 
                   break; 
       case 0414 : extraMemCycCount += 2; 
                   break; 
       case 0415 : extraMemCycCount += 2; 
                   break; 
       case 0416 : extraMemCycCount += 2; 
                   break; 
       case 0417 : extraMemCycCount += 2; 
                   break; 
         default : extraMemCycCount += 1; 
     } 

   if (debug && !debugfile) cout << "ExtraMemCycCount:" << extraMemCycCount << endl;
  
   return 0; 
 } 
 
int initiatetrace()
{
	//ofstream fout;

	//char *traceFileName = new char [strlen(objFilePath)+3];
	//strcpy_s(traceFileName, strlen(objFilePath)+1, objFilePath);
	//traceFileName[strlen(objFilePath)-3] = 't'; //substitute .as 
	//traceFileName[strlen(objFilePath)-2] = 'r'; //with .trac
	//traceFileName[strlen(objFilePath)-1] = 'a';
	//traceFileName[strlen(objFilePath)] = 'c';
	//traceFileName[strlen(objFilePath)+1] = 'e';
	//traceFileName[strlen(objFilePath)+2] = 0;

	//fout.open(traceFileName);	//append pdp8tracec.oct
	//fout.close();

	return 0;
}